package com.service.business.conversion.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.service.business.base.dto.LoginUserSession;
import com.service.business.conversion.controller.rep.ConversionCoinsConfigRep;
import com.service.business.conversion.controller.rep.ConvertPageRep;
import com.service.business.conversion.controller.req.ConversionReq;
import com.service.business.conversion.service.ConvertCoinsRecordDetailService;
import com.service.business.conversion.service.ConvertCoinsRecordService;
import com.service.business.conversion.service.ConvertConfigService;
import com.service.business.member.controller.rep.MemberWalletRep;
import com.service.business.member.dto.MemberStoreChange;
import com.service.business.member.service.MemberService;
import com.service.business.member.service.MemberStoreService;
import com.service.business.member.service.MemberWalletService;
import com.service.business.support.Language;
import com.service.business.support.SessionUtil;
import com.service.business.utils.DateUtil;
import com.service.business.utils.I18nUtil;
import com.service.core.exception.CommonException;
import com.service.core.rest.request.PageRequest;
import com.service.core.rest.response.CommonPageRep;
import com.service.dao.enums.CoinsEnum;
import com.service.dao.enums.CommonStatusEnums;
import com.service.dao.enums.MemberJAType;
import com.service.dao.enums.WalletEnums;
import com.service.dao.mapper.ConversionCoinsRecordMapper;
import com.service.dao.model.ConvertCoinsRecord;
import com.service.dao.model.ConvertCoinsRecordDetail;
import com.service.dao.model.MemberStore;
import com.service.dao.model.MemberWallet;
import com.service.utility.common.MathUtil;
import com.service.utility.modelmapper.ModelMapperUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Service("conversionCoinsRecordService")
public class ConvertCoinsRecordServiceImpl extends ServiceImpl<ConversionCoinsRecordMapper, ConvertCoinsRecord> implements ConvertCoinsRecordService {

    @Autowired
    private SessionUtil sessionUtil;

    @Autowired
    private MemberService memberService;
    @Autowired
    private MemberStoreService storeService;
    @Autowired
    private MemberWalletService memberWalletService;
    @Autowired
    private ConvertConfigService convertConfigService;
    @Autowired
    private ConvertCoinsRecordDetailService recordDetailService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void convertCoin(ConversionReq conversionReq, LoginUserSession session) {
        memberService.validSafetyPwd(session.getUserId(), conversionReq.getSafetyPwd());
        ConversionCoinsConfigRep config = convertConfigService.config(conversionReq.getCoins());
        this.verifyConvertAddress(session.getUserId(), conversionReq.getConvertAddress(), config);
        this.verifyConvertNum(conversionReq.getNum(), config);

        Date currentDate = new Date();
        List<ConvertCoinsRecordDetail> recordDetailList = config.getConfig().getSourceCoins().stream().map(item -> {
            MemberStore sourceMemberStore = storeService.getMemberStore(session.getUserId(), item.getCoinsName());
            BigDecimal coinsNum = MathUtil.div(conversionReq.getNum(), item.getCoinsNum());
            if (sourceMemberStore.getNum().compareTo(coinsNum) < 0) {
                throw new CommonException(I18nUtil.getMessage("conversion_business_exception_num_not_enough", Language.LANG.get(), item.getCoinsName()));
            }

            return ConvertCoinsRecordDetail.builder()
                    .sourceCoinsId(sourceMemberStore.getCoinsId())
                    .sourceCoinsName(sourceMemberStore.getCoinsName())
                    .sourceExpendNum(conversionReq.getNum())
                    .sourceCoinNum(coinsNum)
                    .createTime(currentDate)
                    .build();

        }).collect(Collectors.toList());

        //添加合成记录&修改合成目标账户余额
        //若没有地址，默认添加系统给用户配置的erc20充币地址
        if (StringUtils.isBlank(conversionReq.getConvertAddress())) {
            MemberWalletRep memberWalletRep = memberWalletService.initMemberDepositAddress(session.getUserId(), CoinsEnum.USDT, WalletEnums.Type.ERC20);
            conversionReq.setConvertAddress(memberWalletRep.getWalletAddr());
        }

        BigDecimal coinNum = config.getConfig().getCoinsNum().multiply(conversionReq.getNum());
        ConvertCoinsRecord convertCoinsRecord = createCoinsRecord(session, conversionReq.getCoins(),
                conversionReq.getNum(), coinNum, conversionReq.getConvertAddress());

        recordDetailList.forEach(item -> {
            item.setConversionId(convertCoinsRecord.getId());
            MemberStoreChange sourceStoreChange = MemberStoreChange.builder()
                    .memberId(session.getUserId())
                    .coinsId(item.getSourceCoinsId())
                    .coinsName(item.getSourceCoinsName())
                    .jaType(MemberJAType.CONVERSION_EXPEND)
                    .orderId(convertCoinsRecord.getId())
                    .changeNum(item.getSourceCoinNum())
                    .remark(MemberJAType.CONVERSION_EXPEND.getMsg())
                    .build();
            storeService.changeMemberStore(sourceStoreChange);
        });
        recordDetailService.saveBatch(recordDetailList);
    }

    private void verifyConvertAddress(Integer memberId, String address, ConversionCoinsConfigRep config) {

        if (StringUtils.isBlank(address)) {
            return;
        }

        MemberWallet wallet = memberWalletService.getByAddress(memberId, address);
        if (!StringUtils.equals(wallet.getCoinsName(), config.getConfig().getCoinsName())) {
            throw new CommonException(I18nUtil.getMessage("conversion_business_exception_address_coin", Language.LANG.get(), config.getConfig().getCoinsName()));
        }
        WalletEnums.Type walletType = WalletEnums.Type.valueOf(wallet.getWalletType());
        if (!StringUtils.equals(walletType.getName(), config.getWalletType())) {
            throw new CommonException(I18nUtil.getMessage("conversion_business_exception_address_coin", Language.LANG.get(), config.getWalletType()));
        }
    }

    private void verifyConvertNum(BigDecimal convertNum, ConversionCoinsConfigRep config) {
        if (convertNum.compareTo(config.getMinNum()) < 0) {
            throw new CommonException("合成份数小于最小合成份数", "conversion_business_exception_num_less_then_min");
        }

        if (convertNum.remainder(config.getConversionNumMultiple()).compareTo(BigDecimal.ZERO) != 0) {
            throw new CommonException(I18nUtil.getMessage("conversion_business_exception_num_not_equal_multiple", Language.LANG.get(), config.getConversionNumMultiple()));
        }

        BigDecimal memberLastNum = config.getMemberConversionNumDaily().subtract(config.getMemberConversionNum());
        if (convertNum.compareTo(memberLastNum) > 0) {
            throw new CommonException("合成份数超出当天当前会员等级合成最大数量", "conversion_business_exception_num_more_then_limit");
        }

        BigDecimal allLastNum = config.getConversionNumDaily().subtract(config.getAllConversionNum());
        if (allLastNum.compareTo(convertNum) < 0) {
            throw new CommonException("合成份数超出当天全网合成最大数量", "conversion_business_exception_num_more_then_all_limit");
        }

    }

    private ConvertCoinsRecord createCoinsRecord(LoginUserSession userSession, CoinsEnum coins,
                                                 BigDecimal targetNum, BigDecimal coinNum, String convertAddress) {
        ConvertCoinsRecord coinsRecord = ConvertCoinsRecord.builder()
                .memberId(userSession.getUserId())
                .memberAccount(userSession.getAccount())
                .targetCoinsId(coins.getCode())
                .targetCoinsName(coins.getName())
                .convertNum(targetNum)
                .estimateReleaseNum(coinNum)
                .releaseAddress(convertAddress)
                .releaseTime(new Date())
                .createTime(new Date()).build();
        this.save(coinsRecord);
        return coinsRecord;
    }

    @Override
    public CommonPageRep<ConvertPageRep> pageRecord(CoinsEnum coin, PageRequest pageRequest) {
        LoginUserSession session = sessionUtil.getUser();
        Page<ConvertCoinsRecord> page = PageHelper.startPage(pageRequest.getPageNo(), pageRequest.getPageSize());
        list(new QueryWrapper<ConvertCoinsRecord>().lambda()
                .eq(ConvertCoinsRecord::getMemberId, session.getUserId())
                .orderByDesc(ConvertCoinsRecord::getCreateTime));
        return CommonPageRep.buildResultRep(page, page.getResult().stream()
                .map(item -> ModelMapperUtil.map(item, ConvertPageRep.class)).collect(Collectors.toList()));
    }

    @Override
    public BigDecimal getTotalConversionNum(Integer memberId, CoinsEnum coinsEnum, LocalDate someday) {
        Date startTime = DateUtil.getDayOfStart(someday);
        Date endTime = DateUtils.addDays(startTime, 1);
        return baseMapper.sumConversionNum(memberId, coinsEnum.getName(), startTime, endTime);
    }

}
